<template>
    <el-upload class="uploader" :action="uploadAction" :show-file-list="false" :on-change="onFileChange" accept="image/*" :auto-upload="false" :multiple="false">
        <div class="preview-list">
            <template v-if="previewList.length">
                <img v-for="(item, index) in previewList" :key="index" :src="item" style="width:auto;height:120px;" :class="{single: mode === 'single'}"/>
            </template>
            <el-icon v-if="canAdd" class="uploader-icon">
                <Plus/>
            </el-icon>
        </div>
    </el-upload>

    <!-- 裁剪弹窗 -->
    <el-dialog v-model="cropperState.visible" title="裁剪图片" width="600px" append-to-body>
        <cropper class="cropper" :src="cropperState.tempImg" :stencil-props="{ aspectRatio }" ref="cropperRef"/>
        <template #footer>
            <el-button @click="cropperState.visible = false">取消</el-button>
            <el-button type="primary" @click="handleCrop">确认裁剪</el-button>
        </template>
    </el-dialog>
</template>

<script setup>
import { ref, reactive, computed } from 'vue'
import axios from 'axios'
import { Cropper } from 'vue-advanced-cropper'
import 'vue-advanced-cropper/dist/style.css'

/**
 * Props
 * mode: 'single' | 'multiple'
 */
const props = defineProps({
    modelValue: { type: [Array, String], default: '' },
    uploadAction: { type: String, default: () => `${import.meta.env.VITE_UPLOAD_IMAGE_URL}` },
    headers: { type: Object, default: () => ({}) },
    aspectRatio: { type: Number, default: 5 / 4 },
    mode: { type: String, default: 'multiple' }, // 'single' 或 'multiple'
    max: { type: Number, default: 10 }           // multiple 模式最大数量
})
const emit = defineEmits(['update:modelValue', 'success', 'error'])

const cropperState = reactive({
    visible: false,
    tempImg: ''
})
const cropperRef = ref(null)

const previewList = computed(() => {
    if (!props.modelValue) return []
    if (props.mode === 'single') {
        return typeof props.modelValue === 'string' ? [props.modelValue] : []
    } else {
        return Array.isArray(props.modelValue) ? props.modelValue : []
    }
})

const canAdd = computed(() => {
    if (props.mode === 'single') return previewList.value.length < 1
    return previewList.value.length < props.max
})

// 选择文件 -> 打开裁剪
function onFileChange (file) {
    if (!file?.raw) return
    const reader = new FileReader()
    reader.onload = e => {
        cropperState.tempImg = e.target.result
        cropperState.visible = true
    }
    reader.readAsDataURL(file.raw)
}

// 确认裁剪
function handleCrop () {
    const { value: cropper } = cropperRef
    if (!cropper) return
    const { canvas } = cropper.getResult() || {}
    if (!canvas) return

    canvas.toBlob(async blob => {
        if (!blob) return
        const formData = new FormData()
        formData.append('file', blob, 'crop.png')

        try {
            const res = await axios.post(props.uploadAction, formData, {
                headers: { 'Content-Type': 'multipart/form-data', ...props.headers }
            })
            const { code, data } = res.data || {}
            if (code === 200 && data?.url) {

                if (props.mode === 'single') {
                    // 单图模式，直接传字符串
                    emit('update:modelValue', data.url)
                } else {
                    // 多图模式，传数组
                    const newList = [...previewList.value, data.url].slice(0, props.max)
                    emit('update:modelValue', newList)
                }

                emit('success', data.url)
            } else {
                emit('error', res.data)
            }
        } catch (err) {
            emit('error', err)
        } finally {
            cropperState.visible = false
        }
    })
}
</script>

<style scoped>
.uploader {
    cursor: pointer;
    display: flex;
    flex-wrap: wrap;
}

.uploader-icon {
    font-size: 28px;
    color: #8c939d;
    width: 120px;
    height: 120px;
    text-align: center;
    line-height: 120px;
    border: 1px dashed #d9d9d9;
    border-radius: 6px;
}

.preview-list {
    display: flex;
    gap: 10px;
    align-items: center;
    .single {
        border: 1px dashed #d9d9d9;
        border-radius: 6px;
        &:hover {
            border-color: var(--el-color-primary)
        }
    }
}
</style>
